package com.yc.framework.config;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Cache;
import com.yc.common.core.domain.model.LoginUser;
import com.yc.framework.listener.TokenExpirationListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.retry.annotation.EnableRetry;

import java.util.concurrent.TimeUnit;

/**
 * 缓存配置
 */
@Configuration
@EnableRetry
public class CacheConfig {

    /**
     * 本地缓存（Caffeine）中 access_token 的过期时间（单位：分钟）
     */
    @Value("${web.cache.token.local-ttl}")
    private long localTokenTtl;

    /**
     * Caffeine 本地缓存 Bean，用于缓存登录用户信息
     */
    @Bean
    public Cache<String, LoginUser> tokenCache() {
        return Caffeine.newBuilder()
                .expireAfterWrite(localTokenTtl, TimeUnit.MINUTES)
                .maximumSize(10000)
                .recordStats()
                .build();
    }

    /**
     * Redis 过期事件监听容器
     * 用于监听 Redis 中 key 过期事件
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(
            RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }

    /**
     * 令牌过期事件监听器
     * 监听 Redis 中访问令牌过期事件，将用户信息从备份恢复到本地缓存
     */
    @Bean
    public TokenExpirationListener tokenExpirationListener(
            RedisMessageListenerContainer listenerContainer) {
        TokenExpirationListener listener = new TokenExpirationListener(listenerContainer);

        // 监听所有过期事件，在监听器内部过滤访问令牌
        listenerContainer.addMessageListener(
                listener,
                new PatternTopic("__keyevent@*__:expired"));

        return listener;
    }
}